home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Communications
/
PPPMonitor1.16
/
Source
/
ExecMonitor.m
< prev
next >
Wrap
Text File
|
1996-03-07
|
30KB
|
1,042 lines
// -------------------------------------------------------------------------------------
// ExecMonitor.m
// (Indent:4, Tabs:4)
// -------------------------------------------------------------------------------------
// Copyright 1996 Persistent Technologies, Inc. - all rights reserved
// -------------------------------------------------------------------------------------
// This source code comes with no warranty of any kind, and the user assumes all
// responsibility for its use.
// -------------------------------------------------------------------------------------
#import <libc.h>
#import <stdlib.h>
#import <c.h>
#import <errno.h>
#import <ctype.h>
#import <math.h>
#import <sys/param.h>
#import <sys/types.h>
#import <sys/time.h>
#import <sys/wait.h>
#import <sys/resource.h>
#import <dpsclient/dpsNeXT.h>
#import <appkit/Listener.h>
#import <appkit/appkit.h>
#import "PPPMonitor.h"
#import "ExecScrollText.h"
#import "ExecMonitor.h"
#define CHKPPPSOCK
#ifdef CHKPPPSOCK
#import <sys/socket.h>
#import <sys/ioctl.h>
#import <netdb.h>
#import <net/route.h>
#import <net/if.h>
#endif
// -------------------------------------------------------------------------------------
// local vars
static id globalMonitor = (id)nil; // ExecMonitor instance
static int _testMode_ = 0; // test mode flag
// -------------------------------------------------------------------------------------
// ppp 'system' commands
#define SYS_PPP_ISLOADED "/usr/etc/kl_util -s | /bin/grep ' ppp Loaded' "
#define SYS_PPP_ISUP "/usr/etc/ifconfig ppp0 | /bin/grep RUNNING "
#define SYS_PPPD_RUNNING "ps -ax | egrep '(/pppd )' | egrep -v 'egrep' "
// -------------------------------------------------------------------------------------
// play sounds
#define PLAY(S) { if (S) [[Sound findSoundFor:(S)] play]; }
// -------------------------------------------------------------------------------------
// network applications (default when "MiniShelf" default-db is not available)
#define MAX_SHELFSIZE 10
static const char *netAppList = (char*)0;
static const char *netApps[MAX_SHELFSIZE + 1] = { (char*)0 };
static int netAppCount = 0;
// -------------------------------------------------------------------------------------
// misc defines
#define LCURL '{'
#define RCURL '}'
#define LPREN '('
#define RPREN ')'
#define SYSTEM(C) [ExecRunCommand system:(C)]
// -------------------------------------------------------------------------------------
// Mini-Shelf icon cell
@interface TileButtonCell : ButtonCell
{ BOOL _noDots; }
- (void)setDrawDots:(BOOL)flag;
@end
/* highlight rectangle */
static void _hiliteRect(const NXRect *r)
{
PSgsave();
PSsetalpha(0.5);
PSsetgray(NX_WHITE);
PScompositerect(r->origin.x,r->origin.y,r->size.width,r->size.height,NX_SOVER);
PSgrestore();
}
/* load special images used for minishelf */
static NXImage *miniShelfTile = nil, *shelfTile = nil;
static NXImage *miniShelfDots = nil, *shelfDots = nil;
static void _loadMiniShelfIcons(void)
{
{
const char *tile = ICO_SHELFTILE;
BOOL _tile = (tile && !strcasecmp(tile,"yes"))? YES : NO;
if (!miniShelfTile && _tile) miniShelfTile = [NXImage findImageNamed:"tile"];
shelfTile = _tile? miniShelfTile : nil;
}
{
const char *dots = ICO_SHELFDOTS;
BOOL _dots = (dots && !strcasecmp(dots,"yes"))? YES : NO;
if (!miniShelfDots && _dots) miniShelfDots = [NXImage findImageNamed:"dots"];
shelfDots = _dots? miniShelfDots : nil;
}
}
// -------------------------------------------------------------------------------------
// private functions
/* return interval timer value */
long _intervalTime(void)
{
struct timeval tp;
gettimeofday(&tp, NULL);
return tp.tv_sec;
}
/* return start of command line */
const char *_cmdLine(const char *cmd)
{
const char *n = cmd;
while (*n && isspace(*n)) n++;
if ((*n == LPREN) || (*n == LCURL)) {
for (n++; *n && ((*n != RPREN) && (*n != RCURL)); n++);
if (*n) n++;
}
while (*n && isspace(*n)) n++;
return n;
}
/* return command name */
const char *_getCmdFile(char *buff, const char *cmd)
{
const char *n2, *n1 = _cmdLine(cmd);
for (n2 = n1; *n2 && !isspace(*n2); n2++); // scan for space
strncpy(buff, n1, n2 - n1);
buff[n2 - n1] = 0;
return buff;
}
/* return command name */
const char *_getCmdName(char *buff, const char *cmd)
{
const char *n = cmd;
while (*n && isspace(*n)) n++;
if ((*n == LPREN) || (*n == LCURL)) {
char *b = buff;
for (n++ ; *n && ((*n != RPREN) && (*n != RCURL)); n++) *b++ = *n;
*b = 0;
} else {
char tmp[1024], *name = strrchr((char*)_getCmdFile(tmp,n),'/');
strcpy(buff, (name?name+1:tmp));
}
return buff;
}
/* return true if specified file ends with ".app" */
BOOL _cmdIsApp(const char *cmd)
{
char tmp[1024], *n = (char*)_getCmdFile(tmp,cmd);
const char *dot = strrchr(n, '.');
return (!dot || strcmp(dot,".app"))? NO : YES;
}
// -------------------------------------------------------------------------------------
@implementation ExecMonitor
// -------------------------------------------------------------------------------------
/* show message */
- (void)message:(BOOL)isError:(const char*)fmt, ...
{
char msg[1024], *m = msg;
va_list args;
if (isError) { m += strlen(strcpy(m,"ERROR: ")); }
va_start(args, fmt);
vsprintf(m, fmt, args);
va_end(args);
if (isError) {
if ([cmdMessage shouldDrawColor]) [cmdMessage setTextColor:NX_COLORRED];
else [cmdMessage setTextGray:NX_WHITE];
} else {
if ([cmdMessage shouldDrawColor]) [cmdMessage setTextColor:NX_COLORDKGRAY];
else [cmdMessage setTextGray:NX_DKGRAY];
}
[cmdMessage setStringValue:msg];
}
// -------------------------------------------------------------------------------------
// polling/timer support
/* target/action structure */
typedef struct {
BOOL abort;
id self;
SEL meth;
void *arg;
DPSTimedEntry timerTag;
} _timerHandlerData_t;
/* periodic timer handler */
static void _timerHandler(DPSTimedEntry tag, double now, void *userData)
{
_timerHandlerData_t *t = (_timerHandlerData_t*)userData;
if (t->abort || ![t->self perform:t->meth with:(id)(t->arg)]) {
[t->self perform:@selector(_stopTimer:) with:(id)t];
}
}
/* start periodic timer */
- (_timerHandlerData_t*)_startTimer:(SEL)theSelector arg:(void*)arg freq:(float)freq
{
_timerHandlerData_t *t = (_timerHandlerData_t*)malloc(sizeof(_timerHandlerData_t));
t->abort = NO;
t->self = self;
t->meth = theSelector;
t->arg = arg;
t->timerTag = DPSAddTimedEntry((double)freq,_timerHandler,(void*)t,30);
return t;
}
/* stop timer */
- (void)_stopTimer:(_timerHandlerData_t*)t
{
if (t) {
DPSRemoveTimedEntry(t->timerTag);
free((void*)t);
}
}
/* abbreviated delayed perform */
- (void)_perform:(SEL)sel:(id)objId delay:(int)delay
{
[self perform:sel with:objId afterDelay:delay cancelPrevious:YES];
}
// -------------------------------------------------------------------------------------
// fill mini-shelf
/* fill mini-shelf */
- (void)_fillMiniShelf
{
int i;
static ButtonCell *tileProto = nil;
/* reset/load shelf tile icon */
_loadMiniShelfIcons();
/* clear matrix */
[appMatrix removeRowAt:0 andFree:YES];
/* parse/count shelf apps */
if (CMD_MINISHELF) {
char *n = (char*)(netAppList=STRDUP(CMD_MINISHELF));
netAppCount = 0;
while (*n) {
char *b1, *b2;
while ((*n == ';') || isspace(*n)) *(n++) = 0;
if (!*n) break;
netApps[netAppCount++] = b1 = n;
while (*n && (*n != ';')) n++;
b2 = n - 1;
if (*n) *n++ = 0;
while ((b2 > b1) && isspace(*b2)) *b2-- = 0;
if (netAppCount > MAX_SHELFSIZE) break;
}
netApps[netAppCount] = (char*)nil;
}
/* set prototype */
if (!tileProto) {
// ButtonCell *proto = [appMatrix prototype];
tileProto = [[TileButtonCell alloc] initIconCell:"NXAppTile"];
[tileProto setHighlightsBy:NX_NONE];
[appMatrix setPrototype:tileProto];
}
/* create app buttons */
[appMatrix renewRows:1 cols:netAppCount];
for (i = 0; i < netAppCount; i++) {
id btnCell = [appMatrix cellAt:0:i];
if (!netApps[i]) { netAppCount = i; break; }
if (_cmdIsApp(netApps[i])) {
char buff[1024], *cmdFile = (char*)_getCmdFile(buff,netApps[i]);
id appIcon = [[Application workspace] getIconForFile:cmdFile];
[btnCell setIconPosition:NX_ICONONLY];
[btnCell setImage:appIcon];
} else {
char cmdName[128];
_getCmdName(cmdName,netApps[i]);
[btnCell setIconPosition:NX_ICONABOVE];
[btnCell setIcon:"cmdButton"];
[btnCell setTitle:cmdName];
}
[btnCell setTag:i];
}
[appMatrix display];
}
/* called when new user defaults have been loaded */
+ (void)updateFromDefaults
{
if (globalMonitor) [globalMonitor updateFromDefaults];
}
/* called when new user defaults have been loaded */
- (void)updateFromDefaults
{
const char *showSec = FLG_SHOWSECONDS;
if (showSec) {
int val = atoi(showSec);
_showTileTime = ((val >= 0) && (val <= 2))? val : 0;
}
[self _fillMiniShelf];
[self enableConnectButton:nil];
[self tailLogFile:self];
}
// -------------------------------------------------------------------------------------
// object initialization
/* crete single shared instance */
+ (id)sharedPPPMonitor
{
if (!globalMonitor) globalMonitor = [[self alloc] init];
return globalMonitor;
}
/* init */
- init
{
/* init super */
[super init];
exeWindow = (id)nil;
pppLogExeId = nil;
connectExeId = nil;
pingExeId = nil;
_alreadyConnected = NO;
_showTileTime = 0;
_isConnected = NO;
_isDisconnecting = NO;
_shutDown = NO;
_connectTime = 0L;
/* clear timers */
pppdTimer = (void*)nil;
clockTimer = (void*)nil;
ppingTimer = (void*)nil;
checkTimer = (void*)nil;
/* test mode */
{
const char *val = [NSApp readDefault:"_TestMode_"];
_testMode_ = (val && (*val == '1'))? 1 : 0;
if (_testMode_) fprintf(stderr,"WARNING: PPPMonitor running in TEST mode.\n");
}
/* load nib */
if (![NSApp loadNibSection:"ExecMonitor.nib" owner:self]) {
[NSApp errorPanel:"Could not load nib file 'ExecMonitor.nib'\n"
"(Check that the nib file exists and try again)"];
[NSApp delayedFree:self];
return nil;
}
/* spot-check nib loading */
if (!exeWindow || !appMatrix) {
[NSApp errorPanel:"'ExecMonitor.nib' did not load properly\n"
"(The nib file has apparently been corrupted)"];
[NSApp delayedFree:self];
return nil;
}
/* initially clear messages */
[pingMessage setStringValue:""];
[self message:0:""];
/* set connection state */
if ([self isReallyConnected] || [self isPPPDRunning]) _alreadyConnected = YES;
/* set mini-shelf target */
[appMatrix setTarget:self];
[appMatrix setDoubleAction:@selector(runApplication:)];
[appMatrix setAction:(SEL)0];
/* update default settings */
[self updateFromDefaults];
/* init panel */
[exeWindow setFrameAutosaveName:[NSApp appName]];
[exeWindow setDelegate:self];
[exeWindow makeKeyAndOrderFront:(id)nil];
/* already connected */
if (_alreadyConnected) {
[self message:1:"Already connected (connection not made by this PPPMonitor)"];
[NSApp errorPanel:"Already connected:\n"
"PPPMonitor does not currently support\n"
"previously established connections."];
}
return self;
}
/* free */
- _free:(id)sender { return [self free]; }
- free
{
_shutDown = YES;
if (pppLogExeId) [pppLogExeId killCommand];
if (connectExeId || pingExeId) {
if (connectExeId && (connectExeId != self)) {
// terminate ppp connections (actually, should already be terminated);
}
if (pingExeId) [pingExeId killCommand];
[self perform:@selector(_free:) with:self afterDelay:500 cancelPrevious:YES];
return (id)nil;
}
[exeWindow free];
globalMonitor = (id)nil;
return [super free];
}
// -------------------------------------------------------------------------------------
// shutting down
/* can shut down */
- (BOOL)canShutDown
{
return (connectExeId || pingExeId)? NO : YES;
}
/* can shut down */
+ (BOOL)canShutDown
{
return (!globalMonitor || [globalMonitor canShutDown])? YES : NO;
}
/* return shut down state */
- (BOOL)isShuttingDown
{
return _shutDown;
}
/* return connection flag state */
+ (BOOL)shutDown
{
if (globalMonitor && ![globalMonitor isShuttingDown]) [globalMonitor free];
return globalMonitor? NO : YES;
}
// -------------------------------------------------------------------------------------
// connection state checks
/* check for true connection */
- (BOOL)isReallyConnected
{
#ifdef CHKPPPSOCK
/* open socket to check ppp0 */
// (Thanks to Erik Doernenburg for his contributions to this section)
static int sock = -1; // maintain socket connection
if (sock == -1) sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock != -1) {
struct ifreq ifr;
strcpy(ifr.ifr_name, "ppp0");
if (ioctl(sock,SIOCGIFFLAGS,&ifr) != -1) {
return (ifr.ifr_flags & IFF_UP)? YES : NO;
}
}
// fprintf(stdout,"DEBUG - PPPMonitor: ppp0 socket check failed ...\n");
#endif
return SYSTEM(SYS_PPP_ISUP)? NO : YES; // fallback if all else fails
}
/* check for true connection */
- (BOOL)isPPPDRunning
{
return SYSTEM(SYS_PPPD_RUNNING)? NO : YES;
}
/* check for ppp loaded */
- (BOOL)isPPPDriverLoaded
{
return SYSTEM(SYS_PPP_ISLOADED)? NO : YES;
}
/* enable/disable button options */
- (void)enableConnectButton:(id)sender
{
if (sender) { // delay
[btnConnect setEnabled:NO];
[self _perform:@selector(enableConnectButton:):nil delay:1000];
return;
} else
if (!_alreadyConnected) { // set 'Connect' button enabled state
if (!connectExeId && !_isConnected) {
[btnConnect setTitle:"Connect"];
} else
if ( connectExeId && !_isConnected) {
[btnConnect setTitle:"(Stop)"];
} else {
[btnConnect setTitle:"Disconnect"];
}
[btnConnect setEnabled:YES];
} else {
[btnConnect setEnabled:NO];
}
}
// -------------------------------------------------------------------------------------
// connect time keeper
/* create connect time image */
- (void)setConnectTimeIconImage:(int)hh :(int)mm :(int)ss
{
if (_isConnected) {
static NXImage *appImage = (id)nil;
static NXImage *conImage = (id)nil;
if (!appImage) appImage = [[NXImage alloc] initFromSection:"appConnect"];
if (!conImage) conImage = [NXImage findImageNamed:"appConnect"];
if ([appImage lockFocus]) {
char tm[16];
float w, h;
NXPoint orgPt = { 0.0, 0.0 }, txtPt = { 0.0, 6.0 };
[conImage composite:NX_SOVER toPoint:&orgPt];
if ((_showTileTime == 1) || (ss < 0)) sprintf(tm,"%d:%02d", hh, mm);
else sprintf(tm,"%d:%02d:%02d", hh, mm, ss);
PSsetgray(NX_BLACK);
PSselectfont("Ohlfs",9.0);
PSstringwidth(tm,&w,&h);
txtPt.x = ((48.0 - w) / 2.0) + 1.0;
PSmoveto(txtPt.x,txtPt.y);
PSshow(tm);
[appImage unlockFocus];
[NSApp setApplicationIconImage:appImage];
}
} else {
[NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
}
}
/* TIMER: update connection timer */
- (id)_updClockTime:(id)sender /* 'nil' sender resets timer */
{
if (clockTimer && _isConnected) {
long interval = _intervalTime();
int hh = 0, mm = 0, ss = 0, tt;
static int lastMM = -1, lastMode = -1;
if (!sender || !_connectTime) { _connectTime = interval; }
tt = interval - _connectTime;
hh = tt / 3600, mm = (tt % 3600) / 60, ss = tt % 60;
[self message:0:"Connected (%2d:%02d:%02d)", hh, mm, ss];
if (_showTileTime == 2) {
[self setConnectTimeIconImage:hh:mm:ss];
} else
if ((_showTileTime == 1) && ((lastMM != mm) || (lastMode != 1))) {
[self setConnectTimeIconImage:hh:mm:-1];
lastMM = mm;
} else
if ((_showTileTime == 0) && (lastMode != 0)) {
[NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
}
if (lastMode != _showTileTime) lastMode = _showTileTime;
}
return clockTimer? self : nil;
}
/* start/stop timer */
- (void)_enableConnectClock:(BOOL)flag
{
if (clockTimer) {
[self _stopTimer:(_timerHandlerData_t*)clockTimer];
clockTimer = (void*)nil;
[NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
}
if (flag) {
_timerHandlerData_t *timer;
float freq = 0.99; // just shy of a second
[self _updClockTime:nil]; // reset timer
timer = [self _startTimer:@selector(_updClockTime:) arg:self freq:freq];
clockTimer = (void*)timer;
}
}
// -------------------------------------------------------------------------------------
// periodic command execution
/* TIMER: periodic ping */
- (id)_periodicPing:(id)sender
{
if (!ppingTimer || !_isConnected || !CMD_PING || _testMode_) {
ppingTimer = (void*)nil;
} else
if (!pingExeId) {
[pingMessage setStringValue:"Ping"];
pingExeId = [ExecRunCommand runCommand:CMD_PING output:self];
if (!pingExeId) [pingMessage setStringValue:"****"];
}
return ppingTimer? self : nil;
}
/* start/stop ping */
- (void)_enablePeriodicPing:(BOOL)flag
{
if (ppingTimer) { // cancel if active
[self _stopTimer:(_timerHandlerData_t*)ppingTimer];
ppingTimer = (void*)nil;
if (pingExeId) [pingExeId killCommand];
}
if (flag) {
_timerHandlerData_t *timer;
int sec = CMD_PINGINTERVAL? atoi(CMD_PINGINTERVAL) : 3 * 60;
if (sec < 60) sec = 60;
timer = [self _startTimer:@selector(_periodicPing:) arg:self freq:(float)sec];
ppingTimer = timer;
}
}
// -------------------------------------------------------------------------------------
// connection monitor
/* TIMER: connection established */
- (id)_checkConnect:(id)sender
{
if (!connectExeId || _isConnected) { // if no connection in progress
checkTimer = (void*)nil;
} else
if (_testMode_ || [self isReallyConnected]) { // if connected
PLAY(SND_CONNECT);
_isConnected = YES;
[self enableConnectButton:self]; // delayed
[self _enablePeriodicPing:YES];
[self _enableConnectClock:YES];
checkTimer = (void*)nil;
}
return checkTimer? self : nil;
}
/* start connection check */
- (void)_enableCheckConnect:(BOOL)flag
{
if (flag) {
_timerHandlerData_t *timer;
timer = [self _startTimer:@selector(_checkConnect:) arg:self freq:5.0];
checkTimer = timer;
} else
if (checkTimer) {
[self _stopTimer:(_timerHandlerData_t*)checkTimer];
checkTimer = (void*)nil;
}
}
// -------------------------------------------------------------------------------------
// pppd monitor
/* monitor '/pppd' command */
- (id)_monitorPPPD:(id)sender
{
if (![self isPPPDRunning]) {
pppdTimer = (void*)nil;
[self commandDidComplete:connectExeId withError:0];
}
return pppdTimer? self : nil;
}
/* start monitor */
- (void)_enablePPPDMonitor:(BOOL)flag
{
if (pppdTimer) {
[self _stopTimer:(_timerHandlerData_t*)pppdTimer];
pppdTimer = (void*)nil;
}
if (flag && (connectExeId == self)) {
_timerHandlerData_t *timer;
float freq = 15.0;
timer = [self _startTimer:@selector(_monitorPPPD:) arg:self freq:freq];
pppdTimer = (void*)timer;
}
}
// -------------------------------------------------------------------------------------
// PPP log file monitor
/* print message to ppplog scroll text */
- (void)logMessage:(BOOL)isError:(const char*)fmt, ...
{
va_list args;
/* text color (on a white background) */
if ([[pppLogScroll docView] shouldDrawColor]) {
if (isError) { [pppLogScroll setTextAttributeColor:NX_COLORRED]; }
else { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; }
} else {
if (isError) { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; }
else { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; } // same gray
}
/* print message */
va_start(args, fmt);
[pppLogScroll textPrintf:fmt args:args];
va_end(args);
/* reset color to BLACK */
[pppLogScroll setTextAttributeGray:NX_BLACK];
}
/* tail log file */
- (void)tailLogFile:(id)sender
{
/* initialize pppLogScroll */
if (![pppLogScroll isMemberOf:[ExecScrollText class]]) {
pppLogScroll = [ExecScrollText newExecScrollText:pppLogScroll];
[pppLogScroll setDelegate:self];
[pppLogScroll setTab:[textFont getWidthOf:" "] count:10];
[pppLogScroll textPrintf:"\n"];
[pppLogScroll clearScrollText];
[pppLogScroll setTextAttributeGray:NX_BLACK];
}
/* start tail command */
if (!pppLogExeId) {
[pppLogScroll clearScrollText];
[pppLogScroll setTextAttributeGray:NX_BLACK];
if (CMD_TAILLOG) {
char *ld1 = "/usr/adm/";
char *ld2 = "/private/adm/";
char tailBuff[64], *tail = (char*)CMD_TAILLOG;
if ((strlen(tail) < 32) && !strchr(tail,' ') &&
(!strncmp(tail,ld1,strlen(ld1)) || !strncmp(tail,ld2,strlen(ld2)))) {
sprintf(tailBuff,"/usr/ucb/tail -1f %s;", tail);
tail = tailBuff;
}
[self logMessage:0:"(%s)\n",tail];
pppLogExeId = [pppLogScroll runCommand:tail user:0];
if (!pppLogExeId) [self logMessage:1:"'tail' command failed to start.\n"];
} else {
[self logMessage:1:"'tail' command disabled (not specified).\n"];
}
}
}
/* clear text */
- (void)clearLog:(id)sender
{
[pppLogScroll clearScrollText];
[pppLogScroll setTextAttributeGray:NX_BLACK];
}
// -------------------------------------------------------------------------------------
// user commands
/* connect */
- (void)connect:(id)sender
{
/* disconnect */
if (connectExeId) {
if (!CMD_DISCONNECT) {
[self message:1:"'Disconnect' command has not been specified"];
return;
}
[self message:0:"Disconnecting ... "];
[autoConnect setState:0]; // clear auto-reconnect
[btnConnect setEnabled:NO];
[self _enableCheckConnect:NO]; // cancel connection check
[self _enablePeriodicPing:NO]; // cancel ping
[self _enableConnectClock:NO]; // cancel connection timer
_isDisconnecting = YES;
if (!_testMode_) {
if (SYSTEM(CMD_DISCONNECT)) {
[self message:1:"'Disconnect' command failed"];
_isDisconnecting = NO;
[self enableConnectButton:self]; // delayed
}
} else {
[self commandDidComplete:self withError:0];
}
return;
}
_isDisconnecting = NO;
/* ignore connect-request during shutdown */
if (_shutDown) return;
/* load PPP driver is necessary */
if (!_testMode_ && ![self isPPPDriverLoaded]) {
if (CMD_LOADPPPLKS) {
[self message:0:"Loading PPP driver ... "];
{ [[appMatrix window] flushWindow]; NXPing(); }
if (SYSTEM(CMD_LOADPPPLKS)) {
[self message:1:"Error encountered while loading PPP driver"];
} else {
[self message:0:"Loading PPP driver ... successful"];
}
if (![self isPPPDriverLoaded]) return;
} else {
[self message:1:"PPP driver must be loaded before connecting"];
return;
}
}
/* check for 'Connect' command availability */
if (!CMD_CONNECT) {
[self message:1:"'Connect' command has not been specified"];
return;
}
/* check for already connected */
if (![self isReallyConnected]) {
[self message:0:"Connecting ..."];
_connectStart = _intervalTime();
if (!_testMode_) {
connectExeId = [ExecRunCommand runCommand:CMD_CONNECT output:self];
if (!connectExeId) {
[self message:1:"Connection failed to start"];
} else {
[self _enableCheckConnect:YES];
}
} else {
connectExeId = self;
[self _enableCheckConnect:YES];
}
} else {
_alreadyConnected = YES;
[self message:1:"Already connected"];
}
[self enableConnectButton:self]; // delayed
}
/* run application */
- (void)runApplication:(id)sender
{
id btnCell = [appMatrix selectedCell];
int appNdx = [btnCell tag];
if ((appNdx >= 0) && (appNdx < netAppCount) && netApps[appNdx]) {
const char *app = netApps[appNdx];
int r = -1, c = -1;
NXRect cellFrame;
[appMatrix getRow:&r andCol:&c ofCell:btnCell];
[appMatrix getCellFrame:&cellFrame at:r:c];
[appMatrix lockFocus];
_hiliteRect(&cellFrame);
{ [[appMatrix window] flushWindow]; NXPing(); }
if (_cmdIsApp(app)) {
char buff[1024], *cmdFile = (char*)_getCmdFile(buff,app);
NXPortFromName(cmdFile,(char*)nil);
} else {
const char *cmdLine = _cmdLine(app);
char *ext = ";\n", *cmd = (char*)malloc(strlen(cmdLine)+strlen(ext)+1);
sprintf(cmd,"%s%s",cmdLine,ext);
[ExecMonitor terminalCommand:cmd title:"Command"];
free(cmd);
}
[btnCell setDrawDots:NO];
[btnCell drawSelf:&cellFrame inView:appMatrix];
{ [[appMatrix window] flushWindow]; NXPing(); }
[appMatrix unlockFocus];
} else NXBeep();
}
// -------------------------------------------------------------------------------------
// command execution/completion
// -------------------------------------------------------------------------------------
/* command output (when we are the delegate) */
- (void)commandOutput:(id)execId buffer:(const char*)buffer len:(int)len
{
/* ignore anything we get */
}
/* call-back from shell command (main thread) */
- (void)commandDidComplete:execId withError:(int)err
{
/* check for connection termination */
if (execId == connectExeId) {
_isConnected = NO;
connectExeId = nil;
[NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
if (_isDisconnecting) { // manual disconnect
[self message:0:"Connection terminated"];
} else
if ((_intervalTime() - _connectStart) <= 7) { // failed in <= X seconds
if ([self isPPPDRunning]) { // pppd still running
_alreadyConnected = YES;
[exeWindow makeKeyAndOrderFront:(id)nil];
[self message:1:"'pppd' still running "
"('-detach' apparently not specified)"];
#if 1
[autoConnect setState:0];
#else
// Assuming that '-detach' was not specified on the 'pppd' command:
// At this point the 'pppd' command will be monitored to attempt to detect
// detect when it is destroyed. We attempt to simulate the same interface
// used in 'ExecRunServer' to monitor it's own child processes.
connectExeId = self;
[self _enablePPPDMonitor:YES];
_isConnected = [self isReallyConnected];
return;
#endif
} else {
[self message:1:"Connection did not start properly"];
}
} else
if ([autoConnect state]) { // re-connect
PLAY(SND_DISCONNECT);
[self message:1:"Connection terminated (auto-reconnect)"];
[self _perform:@selector(connect:):nil delay:1000];
} else {
PLAY(SND_DISCONNECT);
[self message:1:"Connection terminated"];
}
_isDisconnecting = NO;
[exeWindow makeKeyAndOrderFront:(id)nil];
[self enableConnectButton:self];
return;
}
/* check for ping */
if (execId == pingExeId) {
pingExeId = nil;
[pingMessage setStringValue:""];
return;
}
/* ppp log scroller */
if (execId == pppLogExeId) {
[self logMessage:err:"'tail' command terminated.\n"];
pppLogExeId = nil;
return;
}
/* error */
fprintf(stderr,"Unknown execId in call to 'commandDidComplete:withError:'");
return;
}
// -------------------------------------------------------------------------------------
// output monitor
// -------------------------------------------------------------------------------------
/* same as "strstr()", except with limit check */
const char *strnstr(const char *s1, const char *s2, int n)
{
int i, n2 = strlen(s2), e = n - n2;
for (i = 0; i < e; i++) {
while ((i < e) && (s1[i] != s2[0])) i++;
if ((i < e) && !strncmp(&s1[i],s2,n2)) return &s1[i];
}
return (char*)0;
}
/* monitor scroll text output */
- (void)monitorOutput:scrollId buffer:(const char*)buffer len:(int)len
{
if (scrollId != pppLogScroll) return;
// not fully implemented
}
// -------------------------------------------------------------------------------------
// Terminal invoke
// -------------------------------------------------------------------------------------
/* invoke command in Terminal window */
+ terminalCommand:(const char*)cmd title:(const char*)title
{
Speaker *speaker;
port_t terminalPort;
/* can't find Terminal */
if (!(terminalPort = NXPortFromName("Terminal", NULL))) return (id)nil;
/* launch Terminal */
[NSApp deactivateSelf];
creat("/tmp/.reallyignorethis.term", 0444);
[[Application workspace] openFile:"/tmp/.reallyignorethis.term"
fromImage:(id)nil at:(NXPoint*)nil inView:(id)nil];
/* run command */
speaker = [NSApp appSpeaker];
[speaker setSendPort: terminalPort];
[speaker selectorRPC:"runCommand:usingShell:inFolder:windowTitle:closeOnExit:"
paramTypes: "cccci", cmd, "", "", title, NO];
return self;
}
// -------------------------------------------------------------------------------------
@end
// -------------------------------------------------------------------------------------
// TileButtonCell
// -------------------------------------------------------------------------------------
@implementation TileButtonCell
- drawSelf:(const NXRect *)cellFrame inView:controlView
{
NXSize imageSize;
NXPoint imageOrigin, iconOrigin = cellFrame->origin;
/* draw tile */
iconOrigin.y += cellFrame->size.height; // assume frameHeight == tileHeight
if (shelfTile) { [shelfTile composite:NX_COPY toPoint:&iconOrigin]; }
else { NXDrawButton(cellFrame, cellFrame); }
/* draw image */
[[self image] getSize:&imageSize];
imageOrigin.x = iconOrigin.x + (cellFrame->size.width - imageSize.width )/2.0;
imageOrigin.y = iconOrigin.y - (cellFrame->size.height - imageSize.height)/2.0;
[[self image] composite:NX_SOVER toPoint:&imageOrigin];
/* draw text */
if ([self iconPosition] == NX_ICONABOVE) {
NXSize titleSize;
NXRect titleRect = *cellFrame;
static Cell *titleCell = nil;
if (!titleCell) {
titleCell = [[Cell alloc] initTextCell:""];
[titleCell setAlignment:NX_CENTERED];
[titleCell setFont:[controlView font]];
}
[titleCell setStringValue:[self title]];
[titleCell calcCellSize:&titleSize inRect:cellFrame];
titleRect.origin.y += titleRect.size.height - titleSize.height - 7;
titleRect.origin.x += floor((titleRect.size.width-titleSize.width)/2.0);
titleRect.size.width = titleSize.width;
titleRect.size.height = titleSize.height;
[titleCell drawSelf:&titleRect inView:controlView];
}
/* draw dots */
if (shelfDots && !_noDots) [shelfDots composite:NX_SOVER toPoint:&iconOrigin];
return self;
}
- highlight:(const NXRect*)cellFrame inView:aView lit:(BOOL)flag { return self; }
- (void)setDrawDots:(BOOL)flag { _noDots = flag? NO : YES; }
@end